package dev.ece.suit.loader;

import java.util.Iterator;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.ConcurrentHashMap;

import dev.ece.util.obj.ObjectCount;

public abstract class Sprite3DFactory implements Observer {

	private final Map<String, ObjectCount<? extends AbstractModel3D<?>>> modelCountMap = new ConcurrentHashMap<String, ObjectCount<? extends AbstractModel3D<?>>>();
	
	public synchronized <T extends Sprite3D<Float>> T loadBoneSprite3D(String name) throws Exception {
		return createBoneSprite3D(name, getBoneModel3D(name));
	}
	
	public synchronized <T extends Sprite3D<Integer>> T loadFrameSprite3D(String name) throws Exception {
		return createFrameSprite3D(name, getFrameModel3D(name));
	}
	
	@SuppressWarnings("unchecked")
	protected <T extends Sprite3D<Float>> T createBoneSprite3D(String name, ObjectCount<BoneModel3D> model) {
		return (T) new Sprite3D<Float>(model);
	}
	
	@SuppressWarnings("unchecked")
	protected <T extends Sprite3D<Integer>> T createFrameSprite3D(String name, ObjectCount<FrameModel3D> model) {
		return (T) new Sprite3D<Integer>(model);
	}
	
	@Override
	@SuppressWarnings("unchecked")
	public synchronized void update(Observable o, Object arg) {
		ObjectCount<? extends AbstractModel3D<?>> model = (ObjectCount<? extends AbstractModel3D<?>>) o;
		if(model.needDestroy()) {
			modelCountMap.remove(model.getName());
			model.getObject().destroy();
		}
	}
	
	@SuppressWarnings("unchecked")
	public synchronized ObjectCount<BoneModel3D> getBoneModel3D(String name) throws Exception {
		ObjectCount<? extends AbstractModel3D<?>> model = modelCountMap.get(name);
		if(model == null) {
			model = createBoneModel3D(name);
			modelCountMap.put(name, model);
			model.addObserver(this);
		}
		model.addCount();
		return (ObjectCount<BoneModel3D>) model;
	}

	@SuppressWarnings("unchecked")
	public synchronized ObjectCount<FrameModel3D> getFrameModel3D(String name) throws Exception {
		ObjectCount<? extends AbstractModel3D<?>> model = modelCountMap.get(name);
		if(model == null) {
			model = createFrameModel3D(name);
			modelCountMap.put(name, model);
			model.addObserver(this);
		}
		model.addCount();
		return (ObjectCount<FrameModel3D>) model;
	}
	
	protected abstract ObjectCount<BoneModel3D> createBoneModel3D(String name) throws Exception;
	
	protected abstract ObjectCount<FrameModel3D> createFrameModel3D(String name) throws Exception;
	
	public void destroy() {
		Iterator<ObjectCount<? extends AbstractModel3D<?>>> it = modelCountMap.values().iterator();
		while(it.hasNext()) {
			it.next().getObject().destroy();
		}
		modelCountMap.clear();
	}
	
}